﻿using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.Domain;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Modularity;
using Framework.SqlSugarCore.Abstractions;
using Framework.SqlSugarCore.Repositories;
using Framework.SqlSugarCore.Uow;
using Microsoft.Extensions.Configuration;
using Yitter.IdGenerator;
using Framework.Core.Extensions;
using System.Collections;
using static System.Collections.Specialized.BitVector32;
using Volo.Abp.Guids;
using Framework.SqlSugarCore.Extensions;

namespace Framework.SqlSugarCore
{
	/// <summary>
	/// SqlSugarCore数据访问基础设施 模块
	/// </summary>
	[DependsOn(typeof(FrameworkSqlSugarCoreAbstractionsModule))] // 集成 用于支持领域驱动设计（DDD）领域层开发 模块
	public class FrameworkSqlSugarCoreModule : AbpModule
	{
		public override Task ConfigureServicesAsync(ServiceConfigurationContext context)
		{
			var service = context.Services;
			var configuration = service.GetConfiguration();
			var section = configuration.GetSection("DbConnOptions");
			Configure<DbConnOptions>(section);
			var dbConnOptions = new DbConnOptions();
			section.Bind(dbConnOptions);

			// 不同的数据库，对于主键的使用规约不一样，需要根据数据库进行判断
			SequentialGuidType guidType;
			switch (dbConnOptions.DbType)
			{
				case DbType.MySql:
				case DbType.PostgreSQL:
					guidType = SequentialGuidType.SequentialAsString;
					break;
				case DbType.SqlServer:
					guidType = SequentialGuidType.SequentialAtEnd;
					break;
				case DbType.Oracle:
					guidType = SequentialGuidType.SequentialAsBinary;
					break;
				default:
					guidType = SequentialGuidType.SequentialAtEnd;
					break;
			}
			Configure<AbpSequentialGuidGeneratorOptions>(options =>
			{
				options.DefaultSequentialGuidType = guidType;
			});

			service.TryAddScoped<ISqlSugarDbContext, SqlSugarDbContextFactory>();

			//不开放sqlsugarClient
			//service.AddTransient<ISqlSugarClient>(x => x.GetRequiredService<ISqlsugarDbContext>().SqlSugarClient);

			service.AddTransient(typeof(IRepository<>), typeof(SqlSugarRepository<>));
			service.AddTransient(typeof(IRepository<,>), typeof(SqlSugarRepository<,>));
			service.AddTransient(typeof(ISqlSugarRepository<>), typeof(SqlSugarRepository<>));
			service.AddTransient(typeof(ISqlSugarRepository<,>), typeof(SqlSugarRepository<,>));

			service.AddTransient(typeof(ISugarDbContextProvider<>), typeof(UnitOfWorkSqlsugarDbContextProvider<>));

			var snowIdSetting = (configuration.GetSection("snowIdSetting")).Get<IdGeneratorOptions>();

			// 注册雪花Id
			YitIdHelper.SetIdGenerator(snowIdSetting);

			// 自定义 SqlSugar 雪花ID算法
			SnowFlakeSingle.WorkId = snowIdSetting.WorkerId;
			StaticConfig.CustomSnowFlakeFunc = () =>
			{
				return YitIdHelper.NextId();
			};

			//替换Sqlsugar默认序列化器，用来解决.Select()不支持嵌套对象/匿名对象的非公有访问器 值无法绑定,如Id属性
			context.Services.AddSingleton<ISerializeService,SqlSugarNonPublicSerializer> ();

			var dbConfig = section.Get<DbConnOptions>();
			//将默认db传递给abp连接字符串模块
			Configure<AbpDbConnectionOptions>(x => { x.ConnectionStrings.Default = dbConfig.Url; });

			context.Services.AddDbContext<DefaultSqlSugarDbContext>();

			return Task.CompletedTask;
		}


		public override async Task OnPreApplicationInitializationAsync(ApplicationInitializationContext context)
		{
			//进行CodeFirst
			var service = context.ServiceProvider;
			var options = service.GetRequiredService<IOptions<DbConnOptions>>().Value;


			// 准备支持多租户种子数据及CodeFirst
			if (options.EnabledCodeFirst)
			{
				CodeFirst(service);
			}
			if (options.EnabledDbSeed)
			{
				await DataSeedAsync(service);
			}
		}

		private void CodeFirst(IServiceProvider service)
		{
			var moduleContainer = service.GetRequiredService<IModuleContainer>();
			var db = service.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient;

			var options = service.GetRequiredService<IOptions<DbConnOptions>>().Value;


			// 初始化/创建数据库
			if (options.DbType != DbType.Oracle)
				db.DbMaintenance.CreateDatabase();

			List<Type> types = new List<Type>();
			foreach (var module in moduleContainer.Modules)
			{
				types.AddRange(module.Assembly.GetTypes()
					.Where(x => x.GetCustomAttribute<IgnoreCodeFirstAttribute>() == null)
					.Where(x => x.GetCustomAttribute<SugarTable>() != null)
					.Where(x => x.GetCustomAttribute<SplitTableAttribute>() is null));
			}
			if (types.Count > 0)
			{
				db.CopyNew().CodeFirst.InitTables(types.ToArray());
			}

		}

		private async Task DataSeedAsync(IServiceProvider service)
		{
			var moduleContainer = service.GetRequiredService<IModuleContainer>();
			var db = service.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient;

			List<Type> seedDataTypes = new List<Type>();
			foreach (var module in moduleContainer.Modules)
			{
				seedDataTypes.AddRange(module.Assembly.GetTypes()
					.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))));
			}

			foreach (var seedType in seedDataTypes)
			{
				var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
				var instance = Activator.CreateInstance(seedType);
				var hasDataMethod = seedType.GetMethod("HasData");
				var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
				if (seedData == null) continue;

				var entityInfo = db.EntityMaintenance.GetEntityInfo(entityType);
				if (entityInfo.Columns.Any(u => u.IsPrimarykey))
				{
					// 按主键进行批量增加和更新
					var storage = db.CopyNew().StorageableByObject(seedData.ToList()).ToStorage();
					storage.AsInsertable.ExecuteCommand();
					storage.AsUpdateable.ExecuteCommand();
				}
				else
				{
					// 无主键则只进行插入
					if (!db.CopyNew().Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any())
						db.CopyNew().InsertableByObject(seedData.ToList()).ExecuteCommand();
				}
			}
		}
	}
}
